﻿using System.Collections.Generic;
using Odin.Runners.CommandLine.Arguments;

namespace Odin.Runners.CommandLine
{
	public class ArgumentManager
	{
		public static readonly ArgumentManager Instance = new ArgumentManager();

		private ShowHelp _showHelp;

		private ArgumentManager()
		{
			_showHelp = new ShowHelp();
			
			Arguments = new List<ArgumentHandler>
			            	{
			            		_showHelp,
								new NoLogo(),
								new OutputXml(),
								new KeepWebServerRunning(),
								new WebSite(),
								new LocalWebSite(),
								new Tests()
			            	};
		}

		public List<ArgumentHandler> Arguments { get; private set; }


		private string GetArgumentName(string argument)
		{
			var pairString = argument.Substring(1);
			if( pairString.Contains("="))
			{
				var pair = argument.Split('=');
				return pair[0].Substring(1);
			} else
			{
				return pairString.Substring(1);
			}
		}

		private KeyValuePair<string,string>	GetArgumentWithParameter(string argument)
		{
			var pairString = argument.Substring(1);
			var pair = argument.Split('=');
			var keyValuePair = new KeyValuePair<string,string>(pair[0].Substring(1), pair[1]);
			return keyValuePair;
		}

		private ArgumentHandler GetArgumentHandler(string arg)
		{
			foreach( var argumentHandler in Arguments )
			{
				if( argumentHandler.Name.Equals(arg) )
				{
					return argumentHandler;
				}
				if( !string.IsNullOrEmpty(argumentHandler.ShortName) &&
					!argumentHandler.Name.Equals(argumentHandler.ShortName) &&
					argumentHandler.ShortName.Equals(arg))
				{
					return argumentHandler;
				}
			}
			return null;
		}

		private bool DoesArgumentsStartWithSlash(string[] args)
		{
			foreach( var arg in args )
			{
				if( arg[0] != '/')
				{
					return false;
				}
			}
			return true;
		}

		private bool DoesAllArgumentsExist(string[] args)
		{
			foreach( var arg in args )
			{
				var name = GetArgumentName(arg);
				var argumentHandler = GetArgumentHandler(name);
				if( null == argumentHandler )
				{
					return false;
				}
			}
			return true;
		}

		private bool DoesArgumentsHaveParametersIfNeeded(string[] args)
		{
			foreach( var arg in args )
			{
				var name = GetArgumentName(arg);
				var argumentHandler = GetArgumentHandler(name);
				if( argumentHandler.NeedsParameter &&
					!arg.Contains("="))
				{
					return false;
				}
			}
			return true;
		}

		private bool AreAnyRequiredArgumentsMissing(string[] args)
		{
			foreach( var argument in Arguments )
			{
				if( argument.Required )
				{
					var found = false;
					foreach( var arg in args )
					{
						var name = GetArgumentName(arg);
						var argumentHandler = GetArgumentHandler(arg);
						if( argumentHandler.Equals(argument) )
						{
							found = true;
							break;
						}
					}
					if( !found )
					{
						return true;
					}
				}
			}
			return false;
		}

		private bool AreArgumentsValid(string[] args)
		{
			var valid = DoesArgumentsStartWithSlash(args) &&
						DoesAllArgumentsExist(args) &&
			            DoesArgumentsHaveParametersIfNeeded(args) &&
						!AreAnyRequiredArgumentsMissing(args);

			return valid;
		}

		private bool IsAnyArgumentHelp(string[] args)
		{
			foreach( var arg in args )
			{
				var name = GetArgumentName(arg);
				var argument = GetArgumentHandler(name);
				if( argument.Equals(_showHelp))
				{
					return true;
				}
			}
			return false;
		}

		private int GetFileIndex(string[] args)
		{
			int fileIndex = -1;
			for (var argIndex = 0; argIndex < args.Length; argIndex++ )
			{
				var arg = args[argIndex];
				if (!arg.StartsWith("/"))
				{
					if( fileIndex != -1 )
					{
						return -1;
					}
					fileIndex = argIndex;
					
				}

			}
			return fileIndex;
		}

		private string[] GetArgumentsWithoutFile(string[] args)
		{
			var fileIndex = GetFileIndex(args);

			File = args[fileIndex];
			var argList = new List<string>();
			argList.AddRange(args);
			argList.Remove(args[fileIndex]);

			args = argList.ToArray();
			return args;
		}


		public bool ValidateArguments(string[] args)
		{
			var fileIndex = GetFileIndex(args);
			if( fileIndex == -1 )
			{
				return false;
			}

			args = GetArgumentsWithoutFile(args);
			if( !AreArgumentsValid(args) || IsAnyArgumentHelp(args) )
			{
				return false;
			}

			return true;
		}



		public void ExecuteArguments(string[] args)
		{
			args = GetArgumentsWithoutFile(args);
			foreach (var arg in args)
			{
				var argument = GetArgumentWithParameter(arg);
				var handler = GetArgumentHandler(argument.Key);
				handler.Execute(argument.Value);
			}
		}


		public string File { get; private set; }

		public void ShowHelp()
		{
			_showHelp.Execute(string.Empty);
		}
	}
}
